# coding=utf-8

from construct import *
from struct import unpack

BNET_HEADER_CONSTANT = "\xff"

SID_GETADVLISTEX = 0x09

class FreeSlotAdapter(Adapter):

    charToInt =   { "0" : 0, "1" : 1, "2" : 2, "3" : 3, "4" : 4, "5" : 5,
                    "6" : 6, "7" : 7, "8" : 8, "9" : 9, "a" : 10, "b" : 11,
                    "c" : 12, "d" : 13, "e" : 14, "f" : 15 }

    intToChar = dict([(v, k) for k, v in charToInt.iteritems()])
                    
    def _encode(self, obj, context):
        return ord(self.intToChar[obj])
 
    def _decode(self, obj, context):
        return self.charToInt[chr(obj)]

GETADVLISTEX = Struct("GetAdvListEx",
                        ULInt32("NumGames"),
                        IfThenElse("data", lambda ctx: ctx["NumGames"] > 0,
                            MetaRepeater(lambda ctx: ctx["NumGames"],
                             Struct("GameAnnounce",
                                ULInt32("GameType"),
                                ULInt32("LanguageID"),
                                ULInt16("AddrFamily"),
                                ULInt16("Port"),
                                ULInt32("HostIp"),
                                ULInt32("sin_zero1"),
                                ULInt32("sin_zero2"),
                                ULInt32("PubPrivate"), #(DWORD) Unknown (Public = 0x10, Private = 0x11)
                                ULInt32("ElapsedGameTime"),
                                CString("GameName"),
                                CString("GamePassword"), # always empty
                                FreeSlotAdapter(Byte("FreeSlots")),     #(BYTE) Free slots (character representing hexadecimal number)
                                String("HostCounter", 8),
                                CString("MapInfo") #encoded
                             )
                            ),
                            ULInt32("Status")
                        )
                    )

class ProtocolError(Exception):
    pass

class Wc3Protocol(object):

    def __init__(self):
        self.buffer = ""

    def dataReceived(self, data):
        self.buffer += data
        buffer = self.buffer
        if len(buffer) < 4:
            #wait for more data
            return
            
        if buffer[0] != BNET_HEADER_CONSTANT:
            raise ProtocolError("Invalid header constant: %r" % buffer[0])
        packetLen = unpack("<H", buffer[2:4])[0]
        
        if len(buffer) < packetLen:
            #wait for more data
            return
            
        packetType = ord(buffer[1])
        packetLen = packetLen - 4 #header size
        buffer = buffer[4:]
        packet = buffer[:packetLen]
        self.buffer = buffer[packetLen:]
        self.rawPacketReceived(packetType, packet)
        
        #cheat reentry without data to see if there are more packets
        self.dataReceived("")

    def rawPacketReceived(self, packetType, rawPacket):
        if packetType == SID_GETADVLISTEX:
            packet = GETADVLISTEX.parse(rawPacket)
            self.announceReceived(packet)
            
          
    def announceReceived(self, packet):
        if packet.NumGames:
            for game in packet.data:
                print game.GameName
                print game.FreeSlots
        
proto = Wc3Protocol()

proto.dataReceived(open("stream.txt", "rb").read())